package ca.scotthyndman.as2haxe.dom;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import ca.scotthyndman.as2haxe.dom.binding.IMethodBinding;
import ca.scotthyndman.as2haxe.dom.visitor.IVisitor;

/**
 * Represents a method call.
 */
public class ASMethodInvocation extends ASExpression {

	/**
	 * The "expression" structural property of this node type.
	 * 
	 * @since 3.0
	 */
	public static final ASChildPropertyDescriptor EXPRESSION_PROPERTY = new ASChildPropertyDescriptor(
			ASMethodInvocation.class, "expression", ASExpression.class, true);

	/**
	 * The "isCast" structural property of this node type.
	 * 
	 * @since 3.0
	 */
	public static final ASSimplePropertyDescriptor IS_CAST_PROPERTY = new ASSimplePropertyDescriptor(
			ASMethodInvocation.class, "isCast", boolean.class, true);

	/**
	 * The "arguments" structural property of this node type.
	 */
	public static final ASChildListPropertyDescriptor ARGUMENTS_PROPERTY = new ASChildListPropertyDescriptor(
			ASMethodInvocation.class, "arguments", ASExpression.class);

	/**
	 * A list of property descriptors (element type:
	 * {@link ASPropertyDescriptor}), or null if uninitialized.
	 */
	private static final List<ASPropertyDescriptor> PROPERTY_DESCRIPTORS;

	static {
		List<Object> properyList = new ArrayList<Object>(4);
		createPropertyList(ASMethodInvocation.class, properyList);
		addProperty(EXPRESSION_PROPERTY, properyList);
		addProperty(IS_CAST_PROPERTY, properyList);
		addProperty(ARGUMENTS_PROPERTY, properyList);
		PROPERTY_DESCRIPTORS = reapPropertyList(properyList);
	}

	/**
	 * Returns a list of structural property descriptors for this node type.
	 * Clients must not modify the result.
	 * 
	 * @return a list of property descriptors (element type:
	 *         {@link ASPropertyDescriptor})
	 */
	public static List<ASPropertyDescriptor> propertyDescriptors() {
		return PROPERTY_DESCRIPTORS;
	}

	//
	// ======== MEMBERS
	//

	private ASExpression expression;

	private boolean isCast = false;

	private List<ASTNode> arguments = new ASTNode.NodeList(ARGUMENTS_PROPERTY);

	//
	// ======== CONSTRUCTION
	//

	/**
	 * Constructs a new instance of ASMethodInvocation
	 * 
	 * @param ast
	 *            The abstract syntax tree that owns this element
	 */
	public ASMethodInvocation(AST ast) {
		super(ast);
	}

	/**
	 * @param expression
	 *            the expression to set
	 */
	public void setExpression(ASExpression expression) {
		if (expression == null) {
			throw new IllegalArgumentException();
		}
		ASTNode oldChild = this.expression;
		preReplaceChild(oldChild, expression, EXPRESSION_PROPERTY);
		this.expression = expression;
		postReplaceChild(oldChild, expression, EXPRESSION_PROPERTY);
	}

	/**
	 * @return the expression
	 */
	public ASExpression getExpression() {
		return expression;
	}

	/**
	 * @return the arguments
	 */
	public List<ASTNode> getArguments() {
		return arguments;
	}

	public boolean isCast() {
		return isCast;
	}

	public void setCast(boolean isCast) {
		preValueChange(IS_CAST_PROPERTY);
		this.isCast = isCast;
		postValueChange(IS_CAST_PROPERTY);
	}

	//
	// ======== INTERNAL GETTER SETTER
	//

	@Override
	boolean internalGetSetBooleanProperty(ASSimplePropertyDescriptor property,
			boolean get, boolean value) {
		if (property == IS_CAST_PROPERTY) {
			if (get) {
				return isCast();
			} else {
				setCast(value);
				return false;
			}
		}
		return super.internalGetSetBooleanProperty(property, get, value);
	}

	@Override
	ASTNode internalGetSetChildProperty(ASChildPropertyDescriptor property,
			boolean get, ASTNode child) {
		if (property == EXPRESSION_PROPERTY) {
			if (get) {
				return getExpression();
			} else {
				setExpression((ASExpression) child);
				return null;
			}
		}
		return super.internalGetSetChildProperty(property, get, child);
	}

	@Override
	List<ASTNode> internalGetChildListProperty(
			ASChildListPropertyDescriptor property) {
		if (property == ARGUMENTS_PROPERTY) {
			return getArguments();
		}

		return super.internalGetChildListProperty(property);
	}

	// ===== BINDINGS

	/**
	 * Returns the binding linked to this method call.
	 */
	public IMethodBinding resolveBinding() {
		return getAST().getBindingManager().resolveMethodBinding(this);
	}

	// ===== TO STRING AND VISITOR

	@Override
	public String toString() {
		StringBuffer ret = new StringBuffer(getExpression().toString() + "(");
		Iterator<ASTNode> itr = getArguments().iterator();
		while (itr.hasNext()) {
			ret.append(itr.next().toString());

			if (itr.hasNext()) {
				ret.append(", ");
			}
		}
		ret.append(")");

		return ret.toString();
	}

	@Override
	public void accept(IVisitor as2Visitor, boolean recursive) {
		recursive = as2Visitor.visit(this);

		if (recursive) {
			getExpression().accept(as2Visitor, recursive);
			for (ASTNode arg : getArguments()) {
				arg.accept(as2Visitor, recursive);
			}
		}
	}
}
